/*
    Copyright (C) 2010-2011 mdis
    
    This file is part of mdis.
    Written by Ketfi-Chérif Mehdi <ketficherif.mehdi@gmail.com>

    mdis is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    mdis is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with mdis.  If not, see <http://www.gnu.org/licenses/>.

*/

#include "global.h"

plugin_t *
 plugin_create ( void )
{
	plugin_t * plugin = NULL;
	plugin = calloc ( 1 , sizeof ( plugin_t ) );
	if ( plugin == NULL )
		return NULL;
	return plugin;
}

uint8_t
 plugin_init ( plugin_t * plugin )
{
	plugin->directory = calloc ( 1 , sizeof (char) );
	if ( plugin->directory == NULL )
		return ERROR_ALLOCATE_MEMORY;
	plugin->libname = calloc ( 1 , sizeof (char) );
	if ( plugin->libname == NULL )
	{
		free ( plugin->directory );
		return ERROR_ALLOCATE_MEMORY;
	}
	return SUCCESS;
}

uint8_t
 plugin_set_directory ( plugin_t * plugin , char * dir , uint32_t size )
{
	char * reallocated = NULL;
	reallocated = realloc ( plugin->directory , ++size * sizeof (char) );
	if ( reallocated == NULL )
		return ERROR_ALLOCATE_MEMORY;
	plugin->directory_size = size;
	plugin->directory = reallocated;
	snprintf ( plugin->directory , plugin->directory_size , "%s" , dir );
	return SUCCESS;
}

uint8_t
 plugin_set ( plugin_t * plugin , char * lib , uint32_t size )
{
	char * reallocated = NULL;
	reallocated = realloc ( plugin->libname , (plugin->directory_size+size+2) * sizeof (char) );
	if ( reallocated == NULL )
		return ERROR_ALLOCATE_MEMORY;
	plugin->libname_size = plugin->directory_size + size + 1;
	plugin->libname = reallocated;
	snprintf ( plugin->libname , plugin->libname_size , "%s/%s" , plugin->directory , lib );
	return SUCCESS;
}

uint8_t
 plugin_search_libs ( plugin_t * plugin )
{
	DIR *plugdir = NULL;
	struct dirent * rplugdir = NULL;
	char *ext = NULL;
	plugdir = opendir ( plugin->directory );
	if ( plugdir == NULL )
	{
		switch ( errno )
		{
			case EACCES:
				printf ( "[!] Permission denied (%s).\n" , plugin->directory );
				break;

			case EBADF:
				printf ( "[!] FD is not a valid file descriptor opened for reading (%s).\n" , plugin->directory );
				break;

			case EMFILE:
				printf ( "[!] Too many file descriptors in use by process (%s).\n" , plugin->directory );
				break;

			case ENFILE:
				printf ( "[!] Too many files are currently open in the system (%s).\n" , plugin->directory );
				break;

			case ENOENT:
				printf ( "[!] '%s' does not exist.\n" , plugin->directory );
				break;

			case ENOMEM:
				printf ( "[!] Insufficient memory to complete the operation (%s).\n" , plugin->directory );
				break;

			case ENOTDIR:
				printf ( "[!] '%s' is not a directory.\n" , plugin->directory );
				break;
		}
		return PLUGIN_ERROR;
	}
	while ( (rplugdir = readdir ( plugdir )) != NULL )
	{
		ext = strchr ( rplugdir->d_name , '.' );
		if ( ext != NULL )
		{
			if ( !strcmp ( ext , ".so" ) )
			{
				printf ( "[x] '%s' seems to be a correct plugin directory.\n" , plugin->directory );
				plugin_set ( plugin , rplugdir->d_name , strlen (rplugdir->d_name) );
				closedir ( plugdir );
				return PLUGIN_FOUND;
			}
		}
	}
	printf ( "[!] Could not find any plugin in '%s'.\n" , plugin->directory );
	closedir ( plugdir );
	return PLUGIN_NOT_FOUND;
}

/*
uint8_t
 plugin_device_supported ( plugin_t * plugin )
{
	DIR *plugdir = NULL;
	struct dirent * rplugdir = NULL;
	char *ext = NULL;
	void * lib = NULL;
	plugin_desc_t * pdesc = NULL;
	plugdir = opendir ( plugin->directory );
	while ( (rplugdir = readdir ( plugdir )) != NULL )
	{
		ext = strchr ( rplugdir->d_name , '.' );
		if ( ext != NULL )
		{
			if ( !strcmp ( ext , ".so" ) )
			{
				lib = dlopen ( plugin->libname , RTLD_LAZY );
				if ( !lib )
				{
					closedir ( plugdir );
					return PLUGIN_LOAD_ERROR;
				}
				pdesc = dlsym ( lib , "desc" );
				if ( pdesc == NULL )
					return 0;
			}
		}
	}
	closedir ( plugdir );
	return PLUGIN_DEVICE_FOUND;
}
*/

uint8_t
 plugin_load_syms ( plugin_t * plugin )
{
	void * funcs = NULL , * pdesc = NULL;
	plugin->handle = dlopen ( plugin->libname , RTLD_LAZY );
	if ( plugin->handle == NULL )
		return PLUGIN_LOAD_ERROR;
	funcs = dlsym ( plugin->handle , "functions" );
	pdesc = dlsym ( plugin->handle , "desc" );
	if ( funcs == NULL || pdesc == NULL )
		return PLUGIN_SYMS_NOT_FOUND;
	plugin->funcs = funcs;
	plugin->pdesc = pdesc;
	return PLUGIN_SYMS_FOUND;
}

void
 plugin_disasm ( plugin_t * plugin , ptui_listview_t listview , uint8_t * code , uint16_t size )
{
	char item[256], tmp[32];
	instruction_t * instruction = NULL;
	uint8_t instr = 0, op1 = 0, op2 = 0;
	uint16_t p = 0, caddr = 0x0000;
	while ( p<size )
	{
		instruction = plugin->funcs->disasm ( code , p );
		if ( !instruction )
			tui_listview_add_item ( listview , "[Unknown opcode]" );
		else
		{
			switch ( instruction->noperands )
			{
				case 0:
					instr = code[p];
					sprintf ( item , " %04X\t%02X\t\t%s" , caddr , instr , instruction->inst );
					tui_listview_add_item ( listview , item );
					break;

				case 1:
					if ( instruction->nbytes_per_operand == 2 )
					{
						instr = code[p];
						op1 = code[p+1];
						op2 = code[p+2];
						sprintf ( tmp , instruction->inst , op1 , op2 );
						sprintf ( item , " %04X\t%02X%02X%02X\t\t%s" , caddr , instr , op1 , op2 , tmp );
						tui_listview_add_item ( listview , item );
						p = p + 2;
						caddr = caddr + 2;
					}
					else if ( instruction->nbytes_per_operand == 1 )
					{
						instr = code[p];
						op1 = code[p+1];
						sprintf ( tmp , instruction->inst , op1 );
						sprintf ( item , " %04X\t%02X%02X\t\t%s" , caddr , instr , op1 , tmp );
						tui_listview_add_item ( listview , item );
						p++;
						caddr++;
					}
					break;

				case 2:
					instr = code[p];
					op1 = code[p+1];
					op2 = code[p+2];
					sprintf ( tmp , instruction->inst , op1 , op2 );
					sprintf ( item , " %04X\t%02X%02X%02X\t\t%s" , caddr , instr , op1 , op2 , tmp );
					tui_listview_add_item ( listview , item );
					p = p + 2;
					caddr = caddr + 2;
					break;
			}
		}
		caddr = caddr + 1;
		p++;
	}
}

void
 plugin_destroy ( plugin_t * plugin )
{
	if ( plugin != NULL )
	{
		if ( plugin->handle != NULL )
			dlclose ( plugin->handle );
		if ( plugin->directory != NULL )
			free ( plugin->directory );
		if ( plugin->libname != NULL )
			free ( plugin->libname );
		free ( plugin );
		plugin = NULL;
	}
}

